import {copyDeep as objCopyDeep} from "@/obj/utils/obj-copy-util.ts"

/**
 * 深拷贝数组
 *
 * @param {Array<any>} origin 原始数组
 * @param {string[]} props 指定对象类型数据中需要拷贝的属性名数组，如果不指定则拷贝对象类型数据所有属性，如果在 props 中指定了要拷贝的属性，
 * 即 props 的长度大于 0，则不在 props 中的属性将不会被拷贝
 * @param {string[]} excludeProps 指定对象类型数据中需要排除拷贝的属性名数组，如果不指定则不排除任何属性，如果相同的属性名同时出现在 props
 * 和 excludeProps 中，excludeProps 优先级更高
 * @param {Record<string, string | string[]>} propsMap 数组中对象类型数据，原始对象中的属性与拷贝目标对象属性的映射关系，即原始对象中指
 * 定属性的属性值要拷贝到目标对象中的哪些属性上，如果要从原始对象拷贝到目标对象上的属性没有指定映射关系，则使用原始对象中的属性名作为目标对象上的属
 * 性名
 * @param {Record<string, (origin: Record<string, any>, originPropName: string, targetPropName: string) => any>} targetPropValMap
 * 数组中对象类型数据，原始对象中属性的属性值拷贝到目标对象的目标属性时，目标对象目标属性与其值之间的映射，或者说是，原始对象属性的属性值拷贝到目标
 * 对象目标属性时的预处理操作，因此需要一个函数，该函数接收目前正在拷贝的原始对象、目前正在拷贝的原始对象属性的属性名和要拷贝到目标对象目标属性的属
 * 性名作为参数
 * @param {(item: any) => boolean} excludeArrItem 数组中元素的排除条件，该函数接收当前遍历到的数组元素作为参数，返回 true 则表示排除该元
 * 素的拷贝，返回 false 则不排除该元素的拷贝，只有当当前遍历到的数组元素不为 undefined、null、function 时才会调用该函数进行判断，默认实现为
 * `() => false`
 * @param {((a: any, b: any) => number) | null} sort 用于指定数组中元素排序的函数，该函数接收两个数组元素作为参数，该函数需要返回一个数
 * 字类型的数据，如果返回的为负数，a 排在 b 前面；如果返回 0，a 和 b 的相对位置不变；如果返回正数，a 排在 b 后面。当 sort 为 null 时，表示不
 * 对数组进行排序，默认实现为 `null`
 * @param {boolean} isCopyEmptyArr 对于数组中对象类型数据，原始对象中属性值为空数组的属性是否进行拷贝，默认值为 true
 * @param {boolean} isRemoveObjUndefined 对于数组中对象类型数据，原始对象中属性值为 undefined 的属性是否移除，不对其进行拷贝，默认值为 false
 * @param {boolean} isRemoveArrUndefined 对于数组中值为 undefined 的元素，是否对其进行移除，不对其进行拷贝，默认值为 false
 * @returns {Array<any>} 深拷贝后得到的数组
 */
export const copyDeep = (
  {
    origin,
    props = [],
    excludeProps = [],
    propsMap = {},
    targetPropValMap = {},
    excludeArrItem = () => false,
    sort = null,
    isCopyEmptyArr = true,
    isRemoveObjUndefined = false,
    isRemoveArrUndefined = false,
  }: {
    origin: Array<any>;
    props?: string[];
    excludeProps?: string[];
    propsMap?: Record<string, string | string[]>;
    targetPropValMap?: Record<string, (origin: Record<string, any>, originPropName: string, targetPropName: string) => any>;
    excludeArrItem?: (item: any) => boolean;
    sort?: ((a: any, b: any) => number) | null;
    isCopyEmptyArr?: boolean;
    isRemoveObjUndefined?: boolean;
    isRemoveArrUndefined?: boolean;
  }
) => {
  const target: Array<any> = []
  // 遍历 origin 数组
  for (let item of origin) {
    // 如果当前遍历的元素的类型为 undefined
    if (item === undefined) {
      // 如果配置了不对数组中的 undefined 元素进行拷贝，则跳过当前元素的拷贝
      if (isRemoveArrUndefined) continue
      target.push(item)
    }
    // 如果当前遍历的元素的类型为 null, function, 则直接赋值
    if (item === null || typeof item === 'function') {
      target.push(item)
    }
    // 根据指定的数组中元素的排除条件进行判断是否跳过当前元素的拷贝
    else if (excludeArrItem(item)) continue
    // 如果当前遍历的属性的类型为数组, 则递归调用自身进行深拷贝
    else if (Array.isArray(item)) {
      target.push(copyDeep({
        origin: item,
        props,
        excludeProps,
        propsMap,
        targetPropValMap,
        excludeArrItem,
        sort,
        isCopyEmptyArr,
        isRemoveObjUndefined,
        isRemoveArrUndefined
      }))
    }
    // 如果当前遍历的属性的类型为对象, 则使用 objCopyDeep 函数进行深拷贝
    else if (typeof item === 'object') {
      target.push(objCopyDeep({
        origin: item,
        props,
        excludeProps,
        propsMap,
        targetPropValMap,
        excludeArrItem,
        sort,
        isCopyEmptyArr,
        isRemoveObjUndefined,
        isRemoveArrUndefined
      }))
    }
    // 其他情况直接赋值
    else {
      target.push(item)
    }
  }
  // 如果需要对数组进行排序
  if (sort) {
    target.sort(sort)
  }
  return target
}
